using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using EmailJabber.EmailJabber.Core.Commands.Common;
using EmailJabber.EmailJabber.Core.EmailLogic;
using EmailJabber.Logic.Commands;

namespace EmailJabber.Logic.Logic.Messengers.EmailMessenger
{
    public class SmtpServerClass:IMessenger
    {
        private const string REPLY_OK = "250 OK";
        private const string REPLY_SYNTAX = "500 Syntax error, command unrecognized";
        private const string REPLY_BAD_ARGS = "501 Syntax error in parameters or arguments";

        private const string EMAIL_SUBJECT_AND_BODY_FORMAT = "{0}  {1}";

        private int _port=10025;

        private string domain;

        public int Port {
            get { return _port; }
            set { _port = value; }
        }
        

        //public event EventHandler<SmtpMessageReceivedEventArgs> MessageReceived;
        public event EventHandler<MessageIM> MessageRecived;


        public SmtpServerClass()
            : this(10025)
        {
            domain = "localhost";
        }

        public SmtpServerClass(int port)
        {
            Port = port;
            domain = "localhost";
            Thread thread = new Thread(MailHandler);
            thread.IsBackground = true;
            thread.Start();
            CommandManager.Instance.Register(new EmailRecived());
        }

        private void MailHandler()
        {
            TcpListener server = new TcpListener(IPAddress.Any, Port);
            server.Start();

            while (true)
            {
                TcpClient client = server.AcceptTcpClient();

                NetworkStream stream = client.GetStream();
                StreamReader reader = new StreamReader(stream, Encoding.ASCII, false);
                StreamWriter writer = new StreamWriter(stream, Encoding.ASCII);

                WriteResponse(writer, "220 {0} Service ready", domain);

                bool quit = false;
                string command;
                SmtpMailMessage message = new SmtpMailMessage();

                while (client.Connected && !quit && (command = reader.ReadLine()) != null)
                {
                    command = command.Trim();
                    if (command.Length >= 4)
                    {
                        switch (command.Substring(0, 4).ToUpper())
                        {
                            case "EHLO":
                                WriteResponse(writer, "250 {0}", IPAddress.Parse(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString()));
                                break;
                            case "HELO":
                                WriteResponse(writer, "250 {0}", IPAddress.Parse(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString()));
                                break;
                            case "MAIL":
                                message = new SmtpMailMessage();
                                if (command.Length > 10 && command.Substring(4, 6).ToUpper() == " FROM:")
                                {
                                    message.From = command.Substring(10);
                                    WriteResponse(writer, REPLY_OK);
                                }
                                else
                                {
                                    WriteResponse(writer, REPLY_BAD_ARGS);
                                }
                                break;
                            case "RCPT":
                                if (command.Length > 8 && command.Substring(4, 4).ToUpper() == " TO:")
                                {
                                    message.AddRecipient(command.Substring(8));
                                    WriteResponse(writer, REPLY_OK);
                                }
                                else
                                {
                                    WriteResponse(writer, REPLY_BAD_ARGS);
                                }
                                break;
                            case "DATA":
                                WriteResponse(writer, "354 Start mail input; end with <CRLF>.<CRLF>");
                                string line;
                                while ((line = reader.ReadLine()) != null && line != ".")
                                {
                                    if (line.Length > 0 && line[0] == '.')
                                    {
                                        line = line.Substring(1);
                                    }
                                    message.AddMessageLine(line);
                                }
                                OnMessageReceived(message);
                                WriteResponse(writer, REPLY_OK);
                                break;
                            case "RSET":
                                message = new SmtpMailMessage();
                                WriteResponse(writer, REPLY_OK);
                                break;
                            case "VRFY":
                                // To support RFC 5321 and since this is a mock, all addresses are valid 
                            case "NOOP":
                                WriteResponse(writer, REPLY_OK);
                                break;
                            case "QUIT":
                                WriteResponse(writer, "221 {0} Service closing transmission channel", domain);
                                quit = true;
                                break;
                            default:
                                // We only implement the minimum command set
                                WriteResponse(writer, REPLY_SYNTAX);
                                break;
                        }
                    }
                    else
                    {
                        // Reply to garbage with
                        WriteResponse(writer, REPLY_SYNTAX);
                    }
                }

                client.Close();
            }
        }

        private void WriteResponse(StreamWriter writer, string format, params object[] arg)
        {
            writer.WriteLine(format, arg);
            writer.Flush();
        }

        private void WriteResponse(StreamWriter writer, string response)
        {
            writer.WriteLine(response);
            writer.Flush();
        }

        public void OnMessageReceived(SmtpMailMessage message)
        {
            MessageRecived.Invoke(this,new MessageIM(message.From,string.Format(EMAIL_SUBJECT_AND_BODY_FORMAT, message.Subject, message.Message),message.To));
        }

        public void Send(MessageIM message)
        {
            
        }
    }
}